package 共享锁实现.javaapilock;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.junit.Test;

import java.io.IOException;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * author Veng Su
 * email  1344114844@qq.com
 * date   18-9-17 上午10:32
 */
/*首先，创建临时有序节点
排序，看自己的节点序号，如果是最小的则拿到锁
如果不是，则自旋等待（会话时间的长度），同时监听自己上一个节点。
当上一个节点解锁即删除节点（会话超时也会超时，这是临时节点的特性），则通知监听者，下一个就可以醒过来，并且拿到锁。test again aaa
  */
public class DistributeLock {
    private final String ROOT_LOCKS = "/locks";

    private ZooKeeper zooKeeper;
    //会话超时时间
    private int sessionTimeout;

    private String lock_id;    //记录锁id

    private final byte[] data = {1, 2};

    private CountDownLatch countDownLatch = new CountDownLatch(1);

    public DistributeLock() throws IOException, InterruptedException {
        this.zooKeeper = ZkClient.getInstance();
        this.sessionTimeout = ZkClient.getSessionTimeout();

    }

    //获取锁的方法
    public synchronized boolean lock()  {
        try {
            //只给 / 是因为创建临时有序节点会自动在后面加上一个序号
            lock_id = zooKeeper.create(ROOT_LOCKS + "/", data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            System.out.println(Thread.currentThread().getName() + "节点id->" + lock_id + "；；；开始竞争");

            List<String> node = zooKeeper.getChildren(ROOT_LOCKS, true);//获取根节点下的所有子节点

            SortedSet<String> strings = new TreeSet<>();
            for (String s : node) {
                strings.add(ROOT_LOCKS + "/" + s);
            }
            String first = strings.first();//拿到最小的节点
            if (lock_id.equals(first)) {
                System.out.println(Thread.currentThread().getName() + "->success,节点id->" + lock_id);

            }
            SortedSet<String> less = strings.headSet(lock_id);//拿到比当前小的lockid
            if (!less.isEmpty()) {
                String preLockid = less.last();
                zooKeeper.exists(preLockid, new LockWatcher(countDownLatch));
                countDownLatch.await(sessionTimeout, TimeUnit.MILLISECONDS);
                System.out.println(Thread.currentThread().getName() + "->success,节点id->" + lock_id);

            }
            return true;
        }catch (Exception e){
            System.out.println("'sdf");
        }

        return false;
    }

    public synchronized boolean unlock() throws KeeperException, InterruptedException {
        try {System.out.println(Thread.currentThread().getName() + "开始释放锁");
        zooKeeper.delete(lock_id, -1);
        System.out.println(Thread.currentThread().getName() + "删除" + lock_id);

        return true;}catch (Exception e){
            System.out.println();
        }
        return false;
    }

    @Test
    public void test() throws InterruptedException {
        final CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
//            Thread.sleep(500);
            new Thread(() -> {
                DistributeLock distributeLock = null;
                try {
                    distributeLock = new DistributeLock();
                    countDownLatch.countDown();
                    countDownLatch.await();
                    distributeLock.lock();
                    Thread.sleep(200);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        distributeLock.unlock();
                        Thread.sleep(2000);
                    } catch (KeeperException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        Thread.sleep(100000);
    }
}
